#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <vector>
#include <algorithm>

//构造函数
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);
    setPortNameComboBox();
    setColor();
    setStopBitComboBox();
    setChkBitComboBox();
    setDataBitComboBox();
    setBaudRateComboBox();
    connect(&serial, SIGNAL(readyRead()), this, SLOT(serialReadyRead()));
    connect(&time, SIGNAL(timeout()), this, SLOT(timerOutSendData()));
}
// 析构函数
Widget::~Widget()
{
    delete ui;
}

// 设置串口的名称
void Widget::setPortNameComboBox()
{
    std::vector<QString> v;
    // 查找系统中可用的串口，存放到vector容器中
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        serial.setPort(info);
        v.push_back(serial.portName());
    }
    std::sort(v.begin(), v.end());    // 重新排序，按串口名称索引从小到大排列
    for (auto i : v)                  // 范围for循环
        ui->comboBoxCom->addItem(i);  // 添加到控件中

    /*
    for (auto iter = v.cbegin(); iter != v.cend(); iter++) {
        qDebug() << *iter;
        ui->comboBoxCom->addItem(*iter);
    }
    */
}
// 设置控件的样式表
void Widget::setColor()
{
    // 设置接收窗口的样式表
    ui->plainTextEditRecv->setStyleSheet("color: white;"  // 字体颜色
                                         "background-color: black;"  // 背景颜色
                                         "selection-color: red;"   // 选中的字体颜色
                                         "selection-background-color: blue;");  // 选中的背景颜色
    // 设置发送窗口的样式表
    ui->plainTextEditSend->setStyleSheet("color: white;"
                                         "background-color: black;"
                                         "selection-color: red;"
                                         "selection-background-color: blue;");
    // 设置uartSet的背景颜色
    QString name = ui->uartSet->objectName();  // 获取对象的名称
    ui->uartSet->setStyleSheet("QWidget#" + name + "{background-color: rgb(51, 204, 255);}");
    // 设置recvSet的背景颜色
    name = ui->recvSet->objectName();
    ui->recvSet->setStyleSheet("QWidget#" + name + "{background-color: rgb(51, 204, 204);}");
    // 设置sendSet的背景颜色
    name = ui->sendSet->objectName();
    ui->sendSet->setStyleSheet("QWidget#" + name + "{background-color: rgb(51, 204, 153);}");
    // 设置openUartColor的颜色，串口关闭时为灰色，打开时为红色
    ui->openUartColor->raise();
    ui->openUartColor->setStyleSheet("QLabel {background-color: rgb(184, 184, 184);}");
}
// 设置控件上显示的停止位和关联数据
void Widget::setStopBitComboBox()
{
    ui->comboBoxStopBit->addItem("1", QSerialPort::OneStop);
    ui->comboBoxStopBit->setCurrentText("1");  // 设置停止位默认显示值位1
    //windows平台上特有
    ui->comboBoxStopBit->addItem("1.5", QSerialPort::OneAndHalfStop);
    ui->comboBoxStopBit->addItem("2", QSerialPort::TwoStop);
    /*
    qint32 index = ui->comboBoxStopBit->findText("2");
    ui->comboBoxStopBit->setCurrentIndex(index);
    */
}
// 设置控件上显示的校验位和关联数据
void Widget::setChkBitComboBox()
{
    ui->comboBoxChkBit->addItem("None", QSerialPort::NoParity);
    ui->comboBoxChkBit->setCurrentText("None");  // 设置校验默认显示值为None
    ui->comboBoxChkBit->addItem("Odd", QSerialPort::OddParity);
    ui->comboBoxChkBit->addItem("Even", QSerialPort::EvenParity);
    ui->comboBoxChkBit->addItem("Mark", QSerialPort::MarkParity);
    ui->comboBoxChkBit->addItem("Space", QSerialPort::SpaceParity);
}
// 设置控件上显示的数据位和关联数据
void Widget::setDataBitComboBox()
{
    ui->comboBoxDataBit->addItem("5", QSerialPort::Data5);
    ui->comboBoxDataBit->addItem("6", QSerialPort::Data6);
    ui->comboBoxDataBit->addItem("7", QSerialPort::Data7);
    ui->comboBoxDataBit->addItem("8", QSerialPort::Data8);
    ui->comboBoxDataBit->setCurrentText("8");  // 设置数据位默认显示值为8
}

// 设置控件上显示的波特率和关联数据
void Widget::setBaudRateComboBox()
{
    ui->comboBoxBaud->addItem("1200", QSerialPort::Baud1200);
    ui->comboBoxBaud->addItem("2400", QSerialPort::Baud2400);
    ui->comboBoxBaud->addItem("4800", QSerialPort::Baud4800);
    ui->comboBoxBaud->addItem("9600", QSerialPort::Baud9600);
    ui->comboBoxBaud->addItem("19200", QSerialPort::Baud19200);
    ui->comboBoxBaud->addItem("38400", QSerialPort::Baud38400);
    ui->comboBoxBaud->addItem("57600", QSerialPort::Baud57600);
    ui->comboBoxBaud->addItem("115200", QSerialPort::Baud115200);
    ui->comboBoxBaud->setCurrentText("115200");  // 设置波特率默认显示值为115200
}

// 设置串口数据位
void Widget::setDataBit(qint32 data)
{
    switch (data) {
    case QSerialPort::Data5:
        serial.setDataBits(QSerialPort::Data5);
        break;
    case QSerialPort::Data6:
        serial.setDataBits(QSerialPort::Data6);
        break;
    case QSerialPort::Data7:
        serial.setDataBits(QSerialPort::Data7);
        break;
    case QSerialPort::Data8:
        serial.setDataBits(QSerialPort::Data8);
        break;
    default:  // 默认数据为8位
        serial.setDataBits(QSerialPort::Data8);
    }
}
// 设置串口校验位
void Widget::setChkBit(qint32 chk)
{
    switch (chk) {
    case QSerialPort::NoParity:
        serial.setParity(QSerialPort::NoParity);
        break;
    case QSerialPort::OddParity:
        serial.setParity(QSerialPort::OddParity);
        break;
    case QSerialPort::EvenParity:
        serial.setParity(QSerialPort::EvenParity);
        break;
    case QSerialPort::MarkParity:
        serial.setParity(QSerialPort::MarkParity);
        break;
    case QSerialPort::SpaceParity:
        serial.setParity(QSerialPort::SpaceParity);
        break;
    default:  // 默认没有校验
        serial.setParity(QSerialPort::NoParity);
        break;
    }
}
// 设置串口停止位
void Widget::setStopBit(qint32 stop)
{
    switch (stop) {
    case QSerialPort::OneStop:
        serial.setStopBits(QSerialPort::OneStop);
        break;
    case QSerialPort::OneAndHalfStop:
        serial.setStopBits(QSerialPort::OneAndHalfStop);
        break;
    case QSerialPort::TwoStop:
        serial.setStopBits(QSerialPort::TwoStop);
        break;
    default:  // 默认一位停止位
        serial.setStopBits(QSerialPort::OneStop);
        break;
    }
}

void Widget::setComboBoxEnable(bool val)
{
    ui->comboBoxCom->setEnabled(val);
    ui->comboBoxBaud->setEnabled(val);
    ui->comboBoxChkBit->setEnabled(val);
    ui->comboBoxDataBit->setEnabled(val);
    ui->comboBoxStopBit->setEnabled(val);
}
// 打开串口的按钮的槽函数
void Widget::on_openUart_toggled(bool checked)
{
    if (checked) {
        // currentData返回的数据类型是QVariant，需要转换
        qint32 baudRate = ui->comboBoxBaud->currentData().toInt();
        QString name = ui->comboBoxCom->currentText();
        qint32 dataBit = ui->comboBoxDataBit->currentData().toInt();
        qint32 chkBit = ui->comboBoxChkBit->currentData().toInt();
        qint32 stopBit = ui->comboBoxStopBit->currentData().toInt();

        serial.setPortName(name);     // 设置串口名称
        serial.setBaudRate(baudRate); // 设置波特率
        setDataBit(dataBit);          // 设置数据位
        setChkBit(chkBit);            // 设置校验位
        setStopBit(stopBit);          // 设置停止位

        serial.open(QIODevice::ReadWrite);    // 以读写的方式打开串口
        ui->openUart->setText("关闭串口");
        ui->openUartColor->setStyleSheet("QLabel {background-color: rgb(255, 0, 0);}");
        setComboBoxEnable(false);
    } else {
        serial.close();
        ui->openUart->setText("打开串口");
        ui->openUartColor->setStyleSheet("QLabel {background-color: rgb(184, 184, 184);}");
        setComboBoxEnable(true);
    }
}

// 清空发送窗口
void Widget::on_clearSend_clicked()
{
    ui->plainTextEditSend->clear();
}
// 清空接收窗口
void Widget::on_clearRecv_clicked()
{
    ui->plainTextEditRecv->clear();
}
// 将十六进制数据流转换为字符串
void Widget::hexToString(QDataStream& stream, QString& string)
{
    QString hexStrTmp;
    while (!stream.atEnd()) {
        qint8 outChar = 0;
        stream >> outChar;
        hexStrTmp = QString("%1").arg(outChar & 0xFF, 2, 16, QLatin1Char('0'));
        string.append(hexStrTmp);
        hexStrTmp = ' ';
        string.append(hexStrTmp);
    }
}

unsigned char Widget::hexToChar(unsigned char hex)
{
    if(hex <= 0x9)
      return (hex + 0x30);
    else if((hex >= 0xA) && (hex <= 0xF))
      return hex + 'A' - 10;
    else return hex - hex;    // 其他值返回0
}
// 将十六进制数据流转换为字符串
void Widget::hexToString(QByteArray& hex, QString& str)
{
    qint32 i = 0, len = hex.size();
    char* data = hex.data();
    unsigned char h, s;
    while (len != 0) {
        h = static_cast<unsigned char>(data[i]);
        s = hexToChar(h >> 4);    // 先转高四位
        str.append(s);

        s = hexToChar(h & 0xF);   // 再转低四位
        str.append(s);
        str.append(' ');  // 每个字节的十六进制数中间用空格隔开
        ++i;
        --len;
    }
}

// 串口readyRead信号的槽函数
void Widget::serialReadyRead()
{
    if (recvDisplayChar == true) { // 接收数据以字符显示
        QString str = serial.readAll();
        ui->plainTextEditRecv->insertPlainText(str);
    } else {  // 接收数据以十六进制显示
#if 0
        // 采用QT自带的方法进行转换
        QString str;
        QByteArray tmp = serial.readAll();
        QDataStream out(&tmp, QIODevice::ReadWrite);
        hexToString(out, str);
        ui->plainTextEditRecv->insertPlainText(str);
#else
        // 直接根据ascii的值进行转换
        QString str;
        QByteArray hex = serial.readAll();
        hexToString(hex, str);
        ui->plainTextEditRecv->insertPlainText(str);
#endif
    }
}

char Widget::charToHex(char ch)
{
    if((ch >= '0') && (ch <= '9'))
      return ch - 0x30;
    else if((ch >= 'A') && (ch <= 'F'))
      return ch - 'A' + 10;
    else if((ch >= 'a') && (ch <= 'f'))
      return ch -'a' + 10;
    else return ch - ch;        //不在0-f范围内的会发送成0
}
// 将字符串转换为十六进制字节流
void Widget::stringToHex(QString &str, QByteArray &sendData)
{
    int hexdata, lowhexdata;
    int hexdatalen = 0;
    int len = str.length();
    sendData.resize(len / 2);
    char lstr, hstr;
    for(int i = 0; i < len;) {
        hstr=str[i].toLatin1();  // 转为acsii
        if(hstr == ' ') {
            i++;
            continue;
        }
        i++;
        if(i >= len)
            break;
        lstr = str[i].toLatin1();
        hexdata = charToHex(hstr);
        lowhexdata = charToHex(lstr);
        hexdata = hexdata * 16 + lowhexdata;
        i++;
        sendData[hexdatalen] = static_cast<char>(hexdata);
        hexdatalen++;
    }
    sendData.resize(hexdatalen);
}

// 单次发送
void Widget::on_onceSend_clicked()
{
    // 读取发送窗口的数据
    QString str = ui->plainTextEditSend->toPlainText();
    QByteArray buffer;
    if (sendChar == true) { // 字符发送
        buffer = str.toUtf8();
        serial.write(buffer);
    }
    else {                 // 十六进制发送
        stringToHex(str, buffer);
        serial.write(buffer);
    }
}
// 定时器超时执行的槽函数
void Widget::timerOutSendData()
{
    on_onceSend_clicked();
}

// 自动发送
void Widget::on_autoSend_clicked(bool checked)
{
    if (true == checked) {
        qint32 t = ui->sendTime->value();
        time.stop();
        time.setInterval(t);
        ui->onceSend->setEnabled(false);
        ui->autoSend->setText("停止自动发送");
        time.start();
    } else {
        time.stop();
        ui->onceSend->setEnabled(true);
        ui->autoSend->setText("自动发送");
    }
}

void Widget::on_sendSetStrDisplay_clicked()
{
    sendChar = true;
}

void Widget::on_sendSetHexDisplay_clicked()
{
    sendChar = false;
}

void Widget::on_recvSetStrDisplay_clicked()
{
    recvDisplayChar = true;
}

void Widget::on_recvSetHexDisplay_clicked()
{
    recvDisplayChar = false;
}

